package fms.acarsMsgProcessUtil;

import fms.util.DateUtil;
import fms.util.ObjectUtil;
import fms.util.StringUtil;
import fms.util.SysConstants;


import java.util.*;
/**
 * Created with IntelliJ IDEA.
 * Date: 2014/7/5
 * Time: 16:55
 * 下行报文报头
 */
public class DownLinkDatagramHeader {

    private String headerString;

    // Line 1
    private Date adccTime;

    // Line 2
    private String priority;
    // 收报用户代码
    private String[] toUserCodes;

    // Line 3
    private String fromCode;
    private String bepTime;

    // Line 4
    private String smi;

    // Line 5
    private FlightID fi;
    public String an;
    private String ma;
    private String tko;
    private String des;
    private String type;
    private String fob;
    private String stime;

    private Map<String, String> tei = new HashMap<String, String>();	// 暂不支持

    // Line 6
    private String dspCode3;
    private String rgs;
    private String rgsTime;
    private MessageStreamNumber msn;

    /// <summary>
    /// 当拆分后msn为空时，则根据值动态生成不重复的内部流水号
    /// </summary>
    private static int msnNum = 0;

    private final String regexGwTime = "[\\s\\S]*\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}[\\s\\S]*";

    public String getHeaderString() {
        return headerString;
    }

    public void setHeaderString(String headerString) {
        this.headerString = headerString;
    }

    public Date getAdccTime() {
        return adccTime;
    }

    public void setAdccTime(Date adccTime) {
        this.adccTime = adccTime;
    }

    public String getRegexGwTime() {
        return regexGwTime;
    }

    public Map<String, String> getTei() {
        return tei;
    }

    public void setTei(Map<String, String> tei) {
        this.tei = tei;
    }

    public String getPriority() {
        return priority;
    }

    public void setPriority(String priority) {
        this.priority = priority;
    }

    public String[] getToUserCodes() {
        return toUserCodes;
    }

    public void setToUserCodes(String[] toUserCodes) {
        this.toUserCodes = toUserCodes;
    }

    public String getFromCode() {
        return fromCode;
    }

    public void setFromCode(String fromCode) {
        this.fromCode = fromCode;
    }

    public String getBepTime() {
        return bepTime;
    }

    public void setBepTime(String bepTime) {
        this.bepTime = bepTime;
    }

    public String getSmi() {
        return smi;
    }

    public void setSmi(String smi) {
        this.smi = smi;
    }

    public FlightID getFi() {
        return fi;
    }

    public void setFi(FlightID fi) {
        this.fi = fi;
    }

    public String getAn() {
        return an;
    }

    public void setAn(String an) {
        this.an = an;
    }

    public String getMa() {
        return ma;
    }

    public void setMa(String ma) {
        this.ma = ma;
    }

    public String getTko() {
        return tko;
    }

    public void setTko(String tko) {
        this.tko = tko;
    }

    public String getDes() {
        return des;
    }

    public void setDes(String des) {
        this.des = des;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getFob() {
        return fob;
    }

    public void setFob(String fob) {
        this.fob = fob;
    }

    public String getStime() {
        return stime;
    }

    public void setStime(String stime) {
        this.stime = stime;
    }

    public String getDspCode3() {
        return dspCode3;
    }

    public void setDspCode3(String dspCode3) {
        this.dspCode3 = dspCode3;
    }

    public String getRgs() {
        return rgs;
    }

    public void setRgs(String rgs) {
        this.rgs = rgs;
    }

    public String getRgsTime() {
        return rgsTime;
    }

    public void setRgsTime(String rgsTime) {
        this.rgsTime = rgsTime;
    }

    public MessageStreamNumber getMsn() {
        return msn;
    }

    public void setMsn(MessageStreamNumber msn) {
        this.msn = msn;
    }

    public static int getMsnNum() {
        return msnNum;
    }

    public static void setMsnNum(int msnNum) {
        DownLinkDatagramHeader.msnNum = msnNum;
    }

    public DownLinkDatagramHeader(String msgHeader) throws Exception {
        this.an = "";
        this.bepTime = "";
        this.dspCode3 = "";
        this.fromCode = "";
        this.headerString = "";
        this.ma = "";
        this.priority = "";
        this.rgs = "";
        this.rgsTime = "";
        this.smi = "";
        this.type = "";
        this.des = "";
        this.tko = "";
        this.fob = "";
        this.stime = "";

        this.fi = new FlightID();
//        this.tei = new Dictionary<String, String>();
        this.msn = new MessageStreamNumber();

        headerString = msgHeader;
        ParseHeader(msgHeader);
    }

    /// <summary>
    /// 拆报头
    /// </summary>
    /// <param name="msg">原始报文或报头字符串</param>
    /// <returns></returns>
    public static DownLinkDatagramHeader Parse(String msg) throws Exception {
        return new DownLinkDatagramHeader(msg);
    }

    private void ParseHeader(String header) throws Exception {
        // 删除报头中最后一个字符（换行符）
        String temp = StringUtil.sideTrim(header.replace("\r\n", "\n"), StringUtil.BACK, "\n");
//System.out.println("ParseHeader-temp：" + temp);
        Object[] linesObj = StringUtil.RemoveEmptyLine(temp.split("\n"));
//System.out.println("ParseHeader-linesObj[0]：" + linesObj[0]);
        String[] lines = ObjectUtil.ObjectToStringArray(linesObj);

        //判断前2行内是否包含时间戳，如果都不包含时间戳，则在最前面加上当前的UTC时间
        if ( !(lines[0].matches(regexGwTime) || lines[1].matches(regexGwTime)) ) {
//            System.out.println(temp);
            temp =  DateUtil.getCurUTCtime(SysConstants.FORMAT_DATETIME_FULL) + "\n" + temp;
//            System.out.println("==========\n" + temp);
            linesObj = StringUtil.RemoveEmptyLine(temp.split("\n"));
            lines = ObjectUtil.ObjectToStringArray(linesObj);
        }

        if (lines.length < 3 || lines.length > 7) {
//            throw new splitHeaderException(
//                    "1000",
//                    header,
//                    "报头必要信息过多或不全", null);
            throw new Exception("报头必要信息过多或不全(报头行数应在4~6行)");
        }

        // 对报头前两行进行拆分
        for (int i = 0; i < 2; i++) {
            if (lines[i].charAt(0) == new Character((char) 1)) {
                splitLine2(lines[i]);
            } else {
                splitLine1(lines[i]);
            }
        }

        // 前三行必有
        try {
            splitLine3(lines[2]);
        } catch (Exception e) {
            throw new Exception("第2行 " + e.getMessage());
        }

        try {
            splitLine4(lines[3]);
        } catch (Exception e) {
            throw new Exception("第3行 " + e.getMessage());
        }

        // 5、6行依情况而定
        if (lines.length >= 5) {
            try {
                splitLine5(lines[4]);
            } catch (Exception e) {
                throw new Exception("第5行 " + e.getMessage());
            }
            if (lines.length >= 6) {
                try {
                    splitLine6(lines[5]);
                } catch (Exception e) {
                    throw new Exception("第6行 " + e.getMessage());
                }
            }
        }
    }

    /// <summary>
    /// AdccTime
    /// </summary>
    /// <param name="line"></param>
    private void splitLine1(String line) throws Exception {
        try {
            adccTime = DateUtil.StrToDate(line, SysConstants.FORMAT_DATETIME_FULL);
        } catch (Exception ex) {
            throw new Exception("拆分620TIME时出错");
//            throw new splitHeaderException(
//                    "1001",
//                    line,
//                    "拆分ADCCTIME时出错", ex);
//            System.out.println("1001:" + line + "拆分ADCCTIME时出错");
//            return;
        }
    }

    /// <summary>
    ///  Priority, ToCodes
    /// </summary>
    /// <param name="line"></param>
    private void splitLine2(String line) throws Exception {
        try {
            if (line.charAt(0) != new Character((char) 1)) { //xuxuxu 20110224   防止windwos2008以上版本不识别'\x01'
//                throw new Exception("缺少报头标识符 '\\x01'");
//                System.out.println("缺少报头标识符 '\\x01'");
//                return;
                throw new Exception("缺少报头标识符'\\x01'");
            }
            priority = line.substring(1, 1 + 2);

            toUserCodes = line.substring(4).split(" ");
        } catch (Exception e) {
//            System.out.println("第2行出错");
//            return;
//            throw new Exception("第2行出错", e);
            throw new Exception("第二行拆分出错：" + e.getMessage());
        }
    }

    // FromCode, BepTime
    private void splitLine3(String line) throws Exception {
        if (line.charAt(0) != '.') {
//                System.out.println("缺少标识符 '.'");
//                return;
//            throw new Exception("缺少标识符 '.'");
            throw new Exception("第三行拆分出错，缺少标识符 '.'");
        }
        fromCode = line.substring(1, 1 + 7);
        bepTime = line.substring(9, 9 + 6);
    }

    // SMI
    private void splitLine4(String line) throws Exception {
        if (line.charAt(0) != new Character((char) 2)) {  //xuxuxu 20110224  防止2008以上版本不识别'\x02'
//            throw new Exception(@"缺少正文标识符 '\x02'");
//            System.out.println("缺少正文标识符 '\\x02'");
//            return;
            throw new Exception("第四行拆分出错，缺少标识符 '\\x02'");
        }

        smi = line.substring(1, 1 + 3);
    }

    // FI, AN, MA, GL. AP, TEI*
    private void splitLine5(String line) throws Exception {
        //如果需要进行两字码归一
        //if (ConvertConfig.IsNeedConvert)
        //{
        //    String[] ruleString = ConvertConfig.ConvertRule;

        //    if (line.IndexOf(ruleString[0]) >= 0)
        //    {
        //        line = line.replace(ruleString[0], ruleString[1]);
        //    }                
        //}

        String[] segments = line.split("/");
        for (String _seg : segments) {
            String seg = _seg.trim();
            if (!seg.equals("")) {
                String[] tokens = seg.split(" ", 2);

                if (tokens.length <= 1) {
                    tei.put(tokens[0], "");
                    continue;
                } else {
                    // 跳过token间可能有的空格
                    tokens[1] = tokens[1].trim();

                    //加入TEI序列
                    tei.put(tokens[0], tokens[1]);
                }

                if (tokens[0].equals("FI")) {
                    fi.AgencyID = tokens[1].substring(0, 0 + 2);
                    fi.FlightNumber = tokens[1].substring(2);
                } else if (tokens[0].equals("AN")) {
                    an = tokens[1];
                    // 去除机尾号前面的句点
                    while (an.startsWith(".")) {
                        an = an.substring(1, 1 + an.length() - 1);
                    }
                    if (an.equals("")) {
//                      throw new Exception("机尾号为空");
//                        System.out.println("机尾号为空");
                        throw new Exception("机尾号为空");
                    }
                    // 分析机尾号组成，将B开头的形式转化为纯数字形式
                    //numericAN = GetNumericAN(an);
                } else if (tokens[0].equals("MA")) {
                    ma = tokens[1];
                } else if (tokens[0].equals("DA")) {
                    tko = tokens[1];
                } else if (tokens[0].equals("AD")) {
                    des = tokens[1];
                } else if (tokens[0].equals("DS")) {
                    des = tokens[1];
                } else if (tokens[0].equals("FB")) {
                    fob = tokens[1];
                } else if (tokens[0].equals("ON")) {
                    type = "ONN";
                    stime = tokens[1];
                } else if (tokens[0].equals("IN")) {
                    type = "INN";
                    stime = tokens[1];
                } else if (tokens[0].equals("OI")) {
                    type = "OUT";
                    stime = tokens[1];
                } else if (tokens[0].equals("OF")) {
                    type = "OFF";
                    stime = tokens[1];
                }
            }	//seg != ""
        }
    }

    // DspCode3, RGS, RgsTime, MSN
    private void splitLine6(String line) throws Exception {
        String[] tokens = line.split(" ");

        int count = tokens.length;
        int pos = 0;

        if (count > pos) {
            while ( tokens[pos].equals("") )
                ++pos;

            if ( !tokens[pos].equals("DT") )  {
//              throw new Exception("机尾号为空");
//                System.out.println("起始标识错误");
//                return;
                throw new Exception("报头第六行（含网关日期）不是DT");
            }

            if (count > ++pos) {
                while (tokens[pos].equals(""))
                    ++pos;
                dspCode3 = tokens[pos];

                if (count > ++pos) {
                    while (tokens[pos].equals("")) ++pos;
                    rgs = tokens[pos];

                    if (count > ++pos) {
                        while (tokens[pos].equals("")) ++pos;
                        rgsTime = tokens[pos];

                        if (count > ++pos) {
                            while (tokens[pos].equals("")) ++pos;
                            String strMsn = tokens[pos];
                            msn.DeviceCode = String.valueOf( strMsn.charAt(0) );
                            msn.StreamNumber = Integer.valueOf(strMsn.substring(1, 1 + 2));
                            msn.BlockNumber = strMsn.substring(3, 3 + 1);
                        }
                    }
                }
            }
        } else {
            throw new Exception("报头第六行（含网关日期）拆分失败");
        }
    }

    private String GenerateMsn(MessageStreamNumber msn) throws Exception {
        if (msn != null && !ObjectUtil.isBlank(msn.toString())) {
            return StringUtil.fillStr(msn.toString(), 4, StringUtil.FRONT, "0");
        }

        if (msnNum >= 10000) {
            msnNum = 0;
        }
        msnNum++;
        return StringUtil.fillStr(msn.toString(), 4, StringUtil.FRONT, "0");
    }

 /*   public String ToXmlString() {
        XmlDocument xmlDoc = new XmlDocument();
        XmlNode nodeHeader = xmlDoc.CreateNode(XmlNodeType.Element, "HEAD", "");
        //xmlDoc.InsertBefore();

        // Line 1
        XmlElement elemAdccTime = xmlDoc.CreateElement("ADCCTime");
        elemAdccTime.InnerText = this.adccTime.toString();
        nodeHeader.AppendChild(elemAdccTime);

        // Line2
        XmlElement elemPriority = xmlDoc.CreateElement("Priority");
        elemPriority.InnerText = this.priority;
        nodeHeader.AppendChild(elemPriority);

        // TODO:
        XmlElement elemtoUserCodes = xmlDoc.CreateElement("ToUserCodes");

        foreach (String code in toUserCodes)
        {
            elemtoUserCodes.InnerText += code + " ";
        }
        nodeHeader.AppendChild(elemtoUserCodes);

        // Line 3
        XmlElement elemFromCode = xmlDoc.CreateElement("FromCode");
        elemFromCode.InnerText = this.fromCode;
        nodeHeader.AppendChild(elemFromCode);

        XmlElement elemBepTime = xmlDoc.CreateElement("BepTime");
        elemBepTime.InnerText = this.bepTime.toString();
        nodeHeader.AppendChild(elemBepTime);

        // Line 4
        XmlElement elemSmi = xmlDoc.CreateElement("SMI");
        elemSmi.InnerText = this.smi;
        nodeHeader.AppendChild(elemSmi);

        // Line 5
        XmlElement elemFi = xmlDoc.CreateElement("FI");
        elemFi.InnerText = this.fi.toString();
        nodeHeader.AppendChild(elemFi);

        XmlElement elemAn = xmlDoc.CreateElement("AN");
        elemAn.InnerText = this.an;
        nodeHeader.AppendChild(elemAn);

        // TODO:
        XmlElement elemTei = xmlDoc.CreateElement("TEI");
        foreach (String key in tei.Keys)
        {
            elemTei.InnerText += String.Concat(key, " ", tei[key], ",");
        }
        elemTei.InnerText = elemTei.InnerText.TrimEnd(',');
        nodeHeader.AppendChild(elemTei);

        // Line 6
        XmlElement elemDspCode3 = xmlDoc.CreateElement("DspCode3");
        elemDspCode3.InnerText = this.dspCode3;
        nodeHeader.AppendChild(elemDspCode3);


        XmlElement elemRgs = xmlDoc.CreateElement("RGS");
        elemRgs.InnerText = this.rgs;
        nodeHeader.AppendChild(elemRgs);


        XmlElement elemRgsTime = xmlDoc.CreateElement("RgsTime");
        elemRgsTime.InnerText = this.rgsTime;
        nodeHeader.AppendChild(elemRgsTime);


        XmlElement elemMsn = xmlDoc.CreateElement("MSN");
        elemMsn.InnerText = this.msn.toString();
        nodeHeader.AppendChild(elemMsn);

        xmlDoc.AppendChild(nodeHeader);

        return xmlDoc.InnerXml;
    }*/

    /// <summary>
    /// 生成一个不重复的报文ID
    /// </summary>
    /// <returns></returns>
    public String GenerateID() throws Exception {
        return DateUtil.DateToStr(adccTime, SysConstants.FORMAT_DATETIME_PURE)
                + StringUtil.fillStr(smi, 3, StringUtil.FRONT, "0")
                + StringUtil.fillStr(an, 6, StringUtil.FRONT, "0")
                + GenerateMsn(msn);
    }
}
